use std::time::{Duration, Instant, SystemTime};

use hmac::{digest::KeyInit, Hmac};
use jsonwebtoken::{encode, EncodingKey};
use jwt::{header::HeaderType, Header, JoseHeader, SignWithKey, Token};
use reqwest::header::{self, HeaderMap, HeaderName, HeaderValue, AUTHORIZATION, CONTENT_TYPE};
use serde::{Deserialize, Serialize};
use sha2::Sha256;

#[derive(Debug, Serialize, Deserialize)]
struct JWTClaim {
    api_key: String,
    exp: u128,
    timestamp: u128,
}

#[derive(Debug, Serialize, Deserialize)]
struct JWTHeader {
    typ: String,
    alg: String,
    sign_type: String,
}

impl Default for JWTHeader {
    fn default() -> Self {
        Self {
            typ: "JWT".into(),
            alg: "HS256".into(),
            sign_type: "SIGN".into(),
        }
    }
}
impl JoseHeader for JWTHeader {
    fn algorithm_type(&self) -> jwt::AlgorithmType {
        jwt::AlgorithmType::Hs256
    }
    fn content_type(&self) -> Option<jwt::header::HeaderContentType> {
        None
    }
    fn key_id(&self) -> Option<&str> {
        None
    }
    fn type_(&self) -> Option<HeaderType> {
        None
    }
}
impl JWTClaim {
    pub fn new(api_id: String, exp_dur: Duration) -> Self {
        let timestamp = SystemTime::now();
        let exp = timestamp + exp_dur;
        Self {
            api_key: api_id,
            timestamp: timestamp
                .duration_since(SystemTime::UNIX_EPOCH)
                .unwrap()
                .as_millis(),
            exp: exp
                .duration_since(SystemTime::UNIX_EPOCH)
                .unwrap()
                .as_millis(),
        }
    }
}

pub fn new_client(api_key: &String, exp_dur: Duration) -> anyhow::Result<reqwest::Client> {
    let mut api_key = api_key.split(".");
    let id = api_key.next().unwrap();
    let secret = api_key.next().unwrap();
    let claims = JWTClaim::new(id.to_owned(), exp_dur);
    let jwt_header = JWTHeader::default();
    let key: Hmac<Sha256> = Hmac::new_from_slice(secret.as_bytes())?;
    let token = Token::new(jwt_header, claims).sign_with_key(&key)?;
    let header = {
        let mut header = HeaderMap::new();
        header.append(AUTHORIZATION, HeaderValue::from_str(token.as_str())?);
        header.append(CONTENT_TYPE, HeaderValue::from_static("application/json"));
        header
    };
    let client = { reqwest::ClientBuilder::new().default_headers(header) }.build()?;
    Ok(client)
}
